package org.mmy.mmybackend.PictureChangType.controller;

import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper;
import lombok.RequiredArgsConstructor;
import org.mmy.mmybackend.PictureChangType.service.PictureService;
import org.mmy.mmybackend.PictureChangType.service.PictureStorageService;
import org.mmy.mmybackend.ServerFileTransfer.entity.File;
import org.mmy.mmybackend.ServerFileTransfer.service.FileService;
import org.mmy.mmybackend.ServerFileTransfer.service.FileStorageService;
import org.mmy.mmybackend.util.Result;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.core.io.Resource;
import org.springframework.core.io.UrlResource;
import org.springframework.http.HttpHeaders;
import org.springframework.http.MediaType;
import org.springframework.http.ResponseEntity;
import org.springframework.stereotype.Controller;
import org.springframework.util.StringUtils;
import org.springframework.web.bind.annotation.*;
import org.springframework.web.multipart.MultipartFile;
import org.springframework.web.servlet.support.ServletUriComponentsBuilder;

import javax.servlet.http.HttpServletRequest;
import java.io.IOException;
import java.net.MalformedURLException;
import java.nio.file.Files;
import java.nio.file.Path;
import java.nio.file.Paths;
import java.nio.file.StandardCopyOption;
import java.time.LocalDateTime;
import java.util.*;
import java.util.concurrent.CompletableFuture;
import java.util.concurrent.TimeUnit;
import java.util.zip.ZipEntry;
import java.util.zip.ZipOutputStream;
import java.nio.file.attribute.BasicFileAttributes;
import java.nio.file.SimpleFileVisitor;
import java.nio.file.FileVisitResult;
import java.util.concurrent.ScheduledExecutorService;
import java.util.concurrent.Executors;
import org.springframework.scheduling.annotation.EnableScheduling;
import org.springframework.scheduling.annotation.Scheduled;
import javax.annotation.PreDestroy;

@Controller
@RequestMapping("/img")
@CrossOrigin(origins = "*") // 允许所有来源（开发环境使用，生产环境建议指定具体域名）
@EnableScheduling
public class PictureController {

    private static final Logger logger = LoggerFactory.getLogger(PictureController.class);
    private static final long FILE_EXPIRATION_TIME = 10; // 文件过期时间（分钟）
    private final ScheduledExecutorService scheduler = Executors.newScheduledThreadPool(1);

    private final PictureStorageService pictureStorageService;
    private final PictureService pictureService;
    private final FileService fileService;

    @Autowired
    public PictureController(PictureStorageService pictureStorageService,
                           PictureService pictureService,
                           FileService fileService) {
        this.pictureStorageService = pictureStorageService;
        this.pictureService = pictureService;
        this.fileService = fileService;
    }

    // 验证图片格式是否支持
    private boolean isValidImageFormat(String format) {
        if (format == null) return false;
        String lowerFormat = format.toLowerCase();
        return Arrays.asList("jpg", "jpeg", "png", "gif", "webp", "bmp").contains(lowerFormat);
    }

    // 清理和验证文件夹名称
    private String sanitizeFolderName(String folderName) {
        if (folderName == null) return null;
        // 移除不安全的字符，只保留字母、数字、下划线和横杠
        return folderName.replaceAll("[^a-zA-Z0-9_-]", "_");
    }

    // 获取文件主名（不含扩展名）
    private String getMainFileName(String filename) {
        if (filename == null) return null;
        int lastDotIndex = filename.lastIndexOf('.');
        return lastDotIndex > 0 ? filename.substring(0, lastDotIndex) : filename;
    }

    // 获取文件扩展名
    private String getExtension(String filename) {
        if (filename == null) return null;
        int lastDotIndex = filename.lastIndexOf('.');
        return lastDotIndex > 0 ? filename.substring(lastDotIndex + 1).toLowerCase() : null;
    }

    /**
     * 上传文件夹中的多个图片并转换为指定格式
     * 前端需要使用 <input type="file" webkitdirectory directory multiple />
     * @param imageFiles 上传的图片文件数组
     * @param targetFormat 目标格式 (e.g., "webp", "png", "jpg")
     * @param outputSubfolder 可选的输出子文件夹名称
     * @return Result 对象，包含转换后文件的信息列表
     */
    @PostMapping("/upload-convert-folder")
    @ResponseBody
    public Result uploadAndConvertFolderImages(
            @RequestParam("files") MultipartFile[] imageFiles,
            @RequestParam("targetFormat") String targetFormat,
            @RequestParam(value = "outputSubfolder", required = false) String outputSubfolder) {

        if (imageFiles == null || imageFiles.length == 0) {
            return Result.FAIL("未选择任何文件", 400);
        }
        if (!isValidImageFormat(targetFormat)) {
            return Result.FAIL("不支持的目标格式: " + targetFormat, 400);
        }

        List<Map<String, Object>> results = new ArrayList<>();
        List<String> errors = new ArrayList<>();
        Path baseStoragePath = Paths.get(pictureStorageService.getFinalUploadPathString());
        
        // 创建唯一的批处理目录
        String batchId = UUID.randomUUID().toString().substring(0, 8);
        Path batchOutputBaseDir = baseStoragePath.resolve("converted_images")
                .resolve(StringUtils.hasText(outputSubfolder) ? 
                        sanitizeFolderName(outputSubfolder) : "batch_" + batchId);

        try {
            Files.createDirectories(batchOutputBaseDir);
        } catch (IOException e) {
            logger.error("Cannot create batch output directory: {}", batchOutputBaseDir, e);
            return Result.FAIL("无法创建输出目录: " + e.getMessage(), 500);
        }

        // 处理每个文件
        for (MultipartFile imageFile : imageFiles) {
            if (imageFile.isEmpty()) {
                errors.add("跳过空文件");
                continue;
            }

            String originalFilename = StringUtils.cleanPath(imageFile.getOriginalFilename());
            String originalFileExtension = getExtension(originalFilename);
            String baseName = getMainFileName(originalFilename);

            if (originalFileExtension == null || !isValidImageFormat(originalFileExtension)) {
                logger.info("跳过非图片文件: {}", originalFilename);
                errors.add("跳过非图片文件: " + originalFilename);
                continue;
            }

            Path tempDir = null;
            Path tempInputPath = null;

            try {
                tempDir = Files.createTempDirectory(baseStoragePath, "temp-multi-");
                String uniqueTempInputName = UUID.randomUUID().toString() + "." + originalFileExtension;
                tempInputPath = tempDir.resolve(uniqueTempInputName);
                imageFile.transferTo(tempInputPath);

                String uniqueOutputName = UUID.randomUUID().toString() + "." + targetFormat.toLowerCase();
                Path finalOutputPath = batchOutputBaseDir.resolve(uniqueOutputName);

                boolean conversionSuccess = pictureService.convertImageFormat(
                        tempInputPath.toString(),
                        finalOutputPath.toString(),
                        targetFormat.toLowerCase()
                );

                if (conversionSuccess) {
                    Map<String, Object> fileResult = new HashMap<>();
                    fileResult.put("originalFilename", originalFilename);
                    fileResult.put("convertedFilename", uniqueOutputName);
                    fileResult.put("status", "成功");
                    results.add(fileResult);

                    // 安排转换后的文件删除
                    scheduleFileDeletion(finalOutputPath);
                } else {
                    errors.add("转换失败: " + originalFilename);
                    Map<String, Object> fileResult = new HashMap<>();
                    fileResult.put("originalFilename", originalFilename);
                    fileResult.put("status", "转换失败");
                    results.add(fileResult);
                }
            } catch (Exception e) {
                logger.error("处理文件时发生错误 {}: {}", originalFilename, e.getMessage(), e);
                errors.add("处理错误 " + originalFilename + ": " + e.getMessage());
                Map<String, Object> fileResult = new HashMap<>();
                fileResult.put("originalFilename", originalFilename);
                fileResult.put("status", "处理错误");
                results.add(fileResult);
            } finally {
                // 清理临时文件
                if (tempInputPath != null && Files.exists(tempInputPath)) {
                    try {
                        Files.delete(tempInputPath);
                    } catch (IOException e) {
                        logger.warn("无法删除临时文件: {}", tempInputPath);
                    }
                }
                if (tempDir != null && Files.exists(tempDir)) {
                    try {
                        Files.delete(tempDir);
                    } catch (IOException e) {
                        logger.warn("无法删除临时目录: {}", tempDir);
                    }
                }
            }
        }

        // 创建ZIP文件
        String zipFileName = "converted_" + batchId + ".zip";
        Path zipFilePath = baseStoragePath.resolve("zips").resolve(zipFileName);

        try {
            Files.createDirectories(zipFilePath.getParent());
            boolean zipSuccess = createZipFile(batchOutputBaseDir, zipFilePath);

            if (zipSuccess) {
                // 创建下载链接
                String zipDownloadUri = ServletUriComponentsBuilder.fromCurrentContextPath()
                        .path("/img/download/zip/")
                        .path(zipFileName)
                        .toUriString();

                Map<String, Object> response = new HashMap<>();
                response.put("processedFiles", results);
                response.put("errors", errors);
                response.put("zipDownloadUrl", zipDownloadUri);

                // 安排ZIP文件和输出目录的删除
                scheduleFileDeletion(zipFilePath);
                scheduleDirDeletion(batchOutputBaseDir);

                if (errors.isEmpty()) {
                    return Result.SUCCESS(response, "所有图片处理完成");
                } else {
                    return Result.SUCCESS(response, "部分图片处理完成，发生了一些错误");
                }
            } else {
                scheduleDirDeletion(batchOutputBaseDir);
                return Result.FAIL("创建压缩文件失败", 500);
            }
        } catch (IOException e) {
            logger.error("创建压缩文件时发生错误: {}", e.getMessage());
            scheduleDirDeletion(batchOutputBaseDir);
            return Result.FAIL("创建压缩文件失败: " + e.getMessage(), 500);
        }
    }

    /**
     * 上传并转换单个图片文件
     * @param imageFile 上传的图片文件
     * @param targetFormat 目标格式 (e.g., "webp", "png", "jpg")
     * @return Result 对象，包含转换后文件的信息
     */
    @PostMapping("/upload-convert")
    @ResponseBody
    public Result uploadAndConvertImage(
            @RequestParam("file") MultipartFile imageFile,
            @RequestParam("targetFormat") String targetFormat) {

        if (imageFile.isEmpty()) {
            return Result.FAIL("未选择任何文件", 400);
        }
        if (!isValidImageFormat(targetFormat)) {
            return Result.FAIL("不支持的目标格式: " + targetFormat, 400);
        }

        String originalFilename = StringUtils.cleanPath(imageFile.getOriginalFilename());
        String originalFileExtension = getExtension(originalFilename);
        String baseName = getMainFileName(originalFilename);

        // 基本检查是否为图片文件
        if (originalFileExtension == null || !isValidImageFormat(originalFileExtension)) {
            return Result.FAIL("不支持的文件类型: " + originalFilename, 400);
        }

        Path baseStoragePath = Paths.get(pictureStorageService.getFinalUploadPathString());
        Path outputDir = baseStoragePath.resolve("converted_images");

        try {
            Files.createDirectories(outputDir);
            
            // 创建临时文件用于存储上传的文件
            String uniqueTempInputName = UUID.randomUUID().toString() + "." + originalFileExtension;
            Path tempInputPath = baseStoragePath.resolve("temp").resolve(uniqueTempInputName);
            Files.createDirectories(tempInputPath.getParent());
            
            // 保存上传的文件
            imageFile.transferTo(tempInputPath);

            // 创建输出文件名和路径
            String uniqueOutputName = UUID.randomUUID().toString() + "." + targetFormat.toLowerCase();
            Path outputPath = outputDir.resolve(uniqueOutputName);

            // 执行转换
            boolean conversionSuccess = pictureService.convertImageFormat(
                    tempInputPath.toString(),
                    outputPath.toString(),
                    targetFormat.toLowerCase()
            );

            // 清理临时文件
            Files.deleteIfExists(tempInputPath);

            if (conversionSuccess) {
                String fileDownloadUri = ServletUriComponentsBuilder.fromCurrentContextPath()
                        .path("/img/download/")
                        .path(uniqueOutputName)
                        .toUriString();

                Map<String, Object> response = new HashMap<>();
                response.put("originalFilename", originalFilename);
                response.put("convertedFilename", uniqueOutputName);
                response.put("downloadUrl", fileDownloadUri);
                response.put("format", targetFormat.toLowerCase());

                return Result.SUCCESS(response, "图片转换成功");
            } else {
                return Result.FAIL("图片转换失败", 500);
            }

        } catch (IOException e) {
            logger.error("文件处理过程中发生错误", e);
            return Result.FAIL("文件处理失败: " + e.getMessage(), 500);
        }
    }

    /**
     * 文件下载接口
     */
    @GetMapping("/download/{filename:.+}")
    public ResponseEntity<Resource> downloadFile(@PathVariable String filename, HttpServletRequest request) {
        Path fileStorageLocation = Paths.get(pictureStorageService.getFinalUploadPathString());
        Path filePath = fileStorageLocation.resolve(filename).normalize();

        // 尝试在常见位置查找文件
        List<Path> potentialPaths = Arrays.asList(
                fileStorageLocation.resolve(filename).normalize(),
                fileStorageLocation.resolve("converted_images").resolve(filename).normalize()
        );

        // 如果URL包含converted_images路径
        if (request.getServletPath().contains("/img/download/converted_images/")) {
            fileStorageLocation = Paths.get(pictureStorageService.getFinalUploadPathString()).resolve("converted_images");
            filename = filename.substring(filename.lastIndexOf('/') + 1);
            filePath = fileStorageLocation.resolve(filename).normalize();
        } else {
            filePath = potentialPaths.stream()
                    .filter(Files::exists)
                    .filter(Files::isReadable)
                    .findFirst()
                    .orElse(fileStorageLocation.resolve(filename).normalize());
        }

        logger.debug("Attempting to download file from path: {}", filePath);

        try {
            Resource resource = new UrlResource(filePath.toUri());
            if (resource.exists() && resource.isReadable()) {
                String contentType = null;
                try {
                    contentType = Files.probeContentType(filePath);
                } catch (IOException ex) {
                    logger.info("Could not determine file type for {}.", filename);
                }
                if (contentType == null) {
                    contentType = "application/octet-stream";
                }
                logger.info("Downloading file: {} with content type: {}", filename, contentType);
                return ResponseEntity.ok()
                        .contentType(MediaType.parseMediaType(contentType))
                        .header(HttpHeaders.CONTENT_DISPOSITION, "attachment; filename=\"" + resource.getFilename() + "\"")
                        .body(resource);
            } else {
                logger.warn("File not found or not readable: {}", filePath);
                return ResponseEntity.notFound().build();
            }
        } catch (MalformedURLException ex) {
            logger.error("Error creating URL for file: {}", filePath, ex);
            return ResponseEntity.status(500).build();
        }
    }

    /**
     * 创建压缩文件
     * @param sourceDir 源文件目录
     * @param zipFile 目标压缩文件
     * @return 是否成功
     */
    private boolean createZipFile(Path sourceDir, Path zipFile) {
        try {
            if (!Files.exists(sourceDir)) {
                logger.error("Source directory does not exist: {}", sourceDir);
                return false;
            }

            try (ZipOutputStream zos = new ZipOutputStream(Files.newOutputStream(zipFile))) {
                Files.walkFileTree(sourceDir, new SimpleFileVisitor<Path>() {
                    @Override
                    public FileVisitResult visitFile(Path file, BasicFileAttributes attrs) throws IOException {
                        // 获取相对路径
                        Path relativePath = sourceDir.relativize(file);
                        ZipEntry zipEntry = new ZipEntry(relativePath.toString());
                        zos.putNextEntry(zipEntry);
                        Files.copy(file, zos);
                        zos.closeEntry();
                        return FileVisitResult.CONTINUE;
                    }
                });
            }
            return true;
        } catch (IOException e) {
            logger.error("Error creating zip file: {}", e.getMessage());
            return false;
        }
    }

    /**
     * 安排文件删除任务
     * @param filePath 要删除的文件路径
     */
    private void scheduleFileDeletion(Path filePath) {
        scheduler.schedule(() -> {
            try {
                if (Files.exists(filePath)) {
                    Files.delete(filePath);
                    logger.info("Deleted expired file: {}", filePath);
                }
            } catch (IOException e) {
                logger.error("Error deleting file {}: {}", filePath, e.getMessage());
            }
        }, FILE_EXPIRATION_TIME, TimeUnit.MINUTES);
    }

    /**
     * 安排目录删除任务
     * @param dirPath 要删除的目录路径
     */
    private void scheduleDirDeletion(Path dirPath) {
        scheduler.schedule(() -> {
            try {
                if (Files.exists(dirPath)) {
                    Files.walkFileTree(dirPath, new SimpleFileVisitor<Path>() {
                        @Override
                        public FileVisitResult visitFile(Path file, BasicFileAttributes attrs) throws IOException {
                            Files.delete(file);
                            return FileVisitResult.CONTINUE;
                        }

                        @Override
                        public FileVisitResult postVisitDirectory(Path dir, IOException exc) throws IOException {
                            Files.delete(dir);
                            return FileVisitResult.CONTINUE;
                        }
                    });
                    logger.info("Deleted expired directory and its contents: {}", dirPath);
                }
            } catch (IOException e) {
                logger.error("Error deleting directory {}: {}", dirPath, e.getMessage());
            }
        }, FILE_EXPIRATION_TIME, TimeUnit.MINUTES);
    }

    /**
     * ZIP文件下载接口
     */
    @GetMapping("/download/zip/{filename:.+}")
    public ResponseEntity<Resource> downloadZipFile(@PathVariable String filename, HttpServletRequest request) {
        Path zipStorageLocation = Paths.get(pictureStorageService.getFinalUploadPathString()).resolve("zips");
        Path zipFilePath = zipStorageLocation.resolve(filename).normalize();

        logger.debug("Attempting to download zip file from path: {}", zipFilePath);

        try {
            Resource resource = new UrlResource(zipFilePath.toUri());
            if (resource.exists() && resource.isReadable()) {
                return ResponseEntity.ok()
                        .contentType(MediaType.APPLICATION_OCTET_STREAM)
                        .header(HttpHeaders.CONTENT_DISPOSITION, "attachment; filename=\"" + resource.getFilename() + "\"")
                        .body(resource);
            } else {
                logger.warn("Zip file not found or not readable: {}", zipFilePath);
                return ResponseEntity.notFound().build();
            }
        } catch (MalformedURLException ex) {
            logger.error("Error creating URL for zip file: {}", zipFilePath, ex);
            return ResponseEntity.status(500).build();
        }
    }

    @PreDestroy
    public void cleanup() {
        scheduler.shutdown();
        try {
            if (!scheduler.awaitTermination(60, TimeUnit.SECONDS)) {
                scheduler.shutdownNow();
            }
        } catch (InterruptedException e) {
            scheduler.shutdownNow();
        }
    }
}
